/**
 * Copyright 2010 Victor Egea Hernando
 *
 * RockScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * RockScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with RockScript.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef ROCKLEXER_H_
#define ROCKLEXER_H_

#include <corelib/types.h>
#include <corelib/ucs/ustring.h>

#include <fstream>

namespace rock{

// token value
typedef union{
    // real value
    corelib::real32 r_;
    // integer value
    corelib::int32 i_;
    // string pointer
    corelib::ustring *s_;
} token_value;

/**
 * @brief Rock language token
 * @author Victor Egea Hernando
 * @date 15/11/2009
 */
typedef struct{
    //! @brief token type
    corelib::uint32 type_;
    //! @brief token value
    token_value val;
} token;

/**
 * @brief Rock language token list.
 * @author Victor Egea Hernando
 * @date 15/11/2009
 */
enum token_type{
    // main keywords
    TK_BREAK = 0,
    TK_CASE,
    TK_CATCH,
    TK_CONTINUE,
    TK_DEFAULT,
    TK_DO,
    TK_ELSE,
    TK_EXTENDS,
    TK_FALSE,
    TK_FOREACH, // 10
    TK_FROM,
    TK_IF,
    TK_IMPORT,
    TK_IN,
    TK_INSTOF,
    TK_NULL,
    TK_OPERATOR,
    TK_PACKAGE,
    TK_RETURN,
    TK_STATIC, // 20
    TK_SWITCH,
    TK_THIS,
    TK_THROW,
    TK_TRUE,
    TK_TRY,
    TK_VAR,
    TK_WHILE, // 27
    // eof
    TK_EOF, // '\0'
    // id, number & string
    TK_CONSTID, // [const-id] -> "$[A-Z]([A-Z|0-9])*"
    TK_CLASSID, // [class-id] -> "[A-Z]([a-z|A-Z|0-9])*"
    TK_ATTRID, // [attr-id] -> "[a-z]([a-z|A-Z|0-9])*_"
    TK_COMMID, // [common-id] -> "[a-z]([a-z|A-Z|0-9])*"
    TK_INT, // [int32]
    TK_REAL, // [real32/64]
    TK_STRING, // [string]
    // aritmetic operators
    TK_ADD, // '+'
    TK_SUB, // '-'
    TK_MLT, // '*'
    TK_DIV, // '/'
    TK_PRC, // '%'
    // urinary tokens
    TK_DPLUS, // '++'
    TK_DSUB, // '--'
    // bitwise operators
    TK_SHFRUS, // '>>>'
    TK_SHFRS, // '>>'
    TK_SHFL, // '<<'
    TK_AND, // '&'
    TK_XOR, // '^'
    TK_NOT, // '~'
    TK_OR, // '|'
    // assignment tokens
    TK_SHFRUSEQ, // '>>>='
    TK_SHFRSEQ, // '>>='
    TK_SHFLEQ, // '<<='
    TK_ADDEQ, // '+='
    TK_SUBEQ, // '-='
    TK_MLTEQ, // '*='
    TK_DIVEQ, // '/='
    TK_MODEQ, // '%='
    TK_ANDEQ, // '&='
    TK_XOREQ, // '^='
    TK_OREQ, // '|='
    TK_EQ, // '='
    // logic tokens
    TK_DAMP, // '&&'
    TK_DVBAR, // '||'
    TK_LESST, // '<'
    TK_GRTT, // '>'
    TK_LESSEQ, // '<='
    TK_GRTEQ, // '=>'
    TK_DEQ, // '=='
    TK_NE, // '!='
    // brackets
    TK_LCRLB, // '{' left curly
    TK_RCRLB, // '}' right curly
    TK_LRNDB, // '(' left round
    TK_RRNDB, // ')' right round
    TK_LSQRB, // '[' left square
    TK_RSQRB, // ']' right square
    // another tokens
    TK_DOT, // '.'
    TK_COLON, // ':'
    TK_DCOLON, // '::'
    TK_SEMC, // ';'
    TK_COM, // ','
    TK_EXCL, // '!'
    TK_QST, // '?'
    TK_ARROP // '[]'
};

/**
 * @brief A handler that reads a script char by char from
 * an unknown source. So the lexer can read the script from
 * you want without unnecessary buffers.
 * @author Victor Egea Hernando
 * @date 15/11/2009
 */
class get_char_hnd {

public:
    /**
     * @brief Gets a char from the script source.
     * @return A char or 0 when reaches the stream end
     */
    virtual char getc( void ) = 0;

};

/**
 * @brief A handler that reads an error from the lexer,
 * it's usually used by the sintactic parser
 * @author Victor Egea Hernando
 * @date 15/11/2009
 */
class lexer_error_hnd {

public:
    /**
     * @brief Commits an error mesage
     * @arg line The line where the error happened
     * @arg column The column where the error happened
     * @arg error An error message
     */
    virtual void lexError( corelib::uint32 line, const corelib::ustring &error ) = 0;

};

/**
 * @brief The lexer of rock language. Decomposes a
 * character stream to rock language tokens.
 * @author Victor Egea Hernando
 * @date 15/11/2009
 */
class lexer {

protected:
    //
    lexer();
    //
    ~lexer();

    // reset the lexer state
    void reset( void );
    //
    void setGetCharHnd( get_char_hnd &hnd );
    //
    void setErrorHnd( lexer_error_hnd &hnd );
    // gets the next token
    token next( void );
    //
    void unexpected( void );

    // lexer line
    corelib::int32 line( void ){ return line_; }

    // gets a number
    void getNumber( token &tk );
    // gets a string
    void getString( token &tk );
    // gets attribute & common ids
    void getRestId( token &tk );
    // gets a class id
    void getClassId( token &tk );
    // gets the rest of a line comment after reading '//'
    void getNLComment( void );
    // gets the rest of a block comment after reading '/*'
    void getComment( void );
    // get the BOM of a utf8 stream
    void getBOM( void );

    // Free the memory of a token
    static void freeTk( token &tk );
    // get token information
    static std::string tk2str( const token &tk );
    // get token representation
    static std::string tkt2str( corelib::uint32 type );

    // returns the keyword index or -1 if it isn't found
    corelib::int32 binarySearch( const char *keyw );
    // call it after reading the EOF
    void eof( void );

    // character handler
    get_char_hnd *getCharHnd_;
    // error handler
    lexer_error_hnd *errorHnd_;
    // character buffer
    std::string charbuff_;
    // current line & column
    corelib::int32 line_;
    // current char
    corelib::uint8 cc_;
    // first time
    bool first_;

    // The parser can use the lexer
    friend class parser;

};

}; // namespace rock

#endif // ROCKLEXER_H_
